Option Strict On

Imports System.Net.Sockets
Imports System.Text

' The UserConnection class encapsulates the functionality of a TcpClient connection with streaming for a single user.
Public Class UserConnection

	Implements IDisposable

	Public Event LineReceived(ByVal Sender As UserConnection, ByVal Data As String)
	Public Event Terminated(ByVal Sender As UserConnection)

	'====================================================================

	Private ReadBuffer(BufferSize) As Byte

	Private Client As TcpClient
	Private Writer As IO.StreamWriter

	'====================================================================

	Public Sub New(ByVal ExistingClient As TcpClient)
		Me.Client = ExistingClient
		Me.Writer = New IO.StreamWriter(Client.GetStream)

		' This starts the asynchronous read thread. The data will be saved into readBuffer.
		Me.Client.GetStream.BeginRead(ReadBuffer, 0, BufferSize, AddressOf ReceiverCallback, Nothing)
	End Sub

	'====================================================================

	' This is the callback function for TcpClient.GetStream.Begin. It begins an asynchronous read from a stream.
	Private Sub ReceiverCallback(ByVal ar As IAsyncResult)
		Dim BytesRead As Integer

		Try
			' Finish asynchronous read into readBuffer and get number of bytes read.
			BytesRead = Client.GetStream.EndRead(ar)

			If BytesRead < 1 Then
				Terminate()				' If no bytes were read server has closed.
				Exit Sub				' No need to continue listening.
			End If

			'====================================================================
			' the old way doesn't support international characters:
			' strMessage = System.Text.Encoding.ASCII.GetString(ReadBuffer, 0, BytesRead) 
			'====================================================================

			' this does support international characters
			Dim strMessage As String = System.Text.Encoding.UTF8.GetString(ReadBuffer, 0, BytesRead)

			' Start a new asynchronous read into readBuffer.
			Client.GetStream.BeginRead(ReadBuffer, 0, BufferSize, AddressOf ReceiverCallback, Nothing)

			RaiseEvent LineReceived(Me, strMessage)

		Catch e As Exception
			Terminate()
		End Try
	End Sub

	'====================================================================
	' Terminating connection and the underlying client
	'====================================================================

	' Initialize termination process when connection was lost
	Private Sub Terminate()
		RaiseEvent Terminated(Me)
	End Sub

	Public Sub Dispose() Implements System.IDisposable.Dispose
		If Not Writer Is Nothing Then
			Writer.Close()
			Writer = Nothing
		End If
		If Not Client Is Nothing Then
			' TODO: Think about shared access to this instance and synchronisation.
			Client.Close()
			Client = Nothing
		End If
	End Sub

	Protected Overrides Sub Finalize()
		MyBase.Finalize()
		Dispose()
	End Sub

	'====================================================================
	' The only public method to send data using this connection.
	'====================================================================

	Public Sub SendData(ByVal Data As String)
		If Writer Is Nothing OrElse Data.Length < 1 Then Return

		Writer.Write(Data)
		Writer.Flush()		 ' Make sure all data is sent now.
	End Sub

End Class
